package com.pine.app.module.security.oauth.filter;

import com.pine.app.module.security.core.Authentication;
import com.pine.app.module.security.core.SecurityContextHolder;
import com.pine.app.module.security.core.session.AuthenticationSessionStore;
import com.pine.app.module.security.core.session.impl.AuthenticationSessionStoreImpl;
import com.pine.app.module.security.core.web.BaseWebFilter;
import com.pine.app.module.security.oauth.provider.AuthenticationManager;
import com.pine.app.module.security.oauth.exception.AuthenticationException;
import com.pine.app.module.security.oauth.provider.authentication.BearerTokenExtractor;
import com.pine.app.module.security.oauth.provider.authentication.TokenExtractor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 上下文处理拦截器
 *
 * @author xiaoyuan
 * @create 2020/3/16 16:24
 **/
@Slf4j
@Order(40)
public class SecurityContextFilter extends BaseWebFilter {

    static final String FILTER_APPLIED = "pine_security_session_mgmt_filter_applied";

    private AuthenticationSessionStore authenticationSessionStore= new AuthenticationSessionStoreImpl();

    private TokenExtractor tokenExtractor = new BearerTokenExtractor();

    private AuthenticationManager authenticate;


    @Autowired
    public void setAuthenticate(AuthenticationManager authenticate) {
        this.authenticate = authenticate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {

        if (request.getAttribute(FILTER_APPLIED) != null) {
            // 如果在当前请求过程中该过滤器已经应用过，则不在二次应用，继续filter chain的执行
            chain.doFilter(request, response);
            return;
        }
        boolean debug = logger.isDebugEnabled();
        // 该过滤器要执行了，在请求上设置该过滤器已经执行过的标记，避免在该请求的同一处理过程中
        // 本过滤器执行二遍
        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
        //判断是否已经登陆
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            try {
                //未登录检查是否带有token
                authentication = this.tokenExtractor.extract(request);
                if (authentication != null) {
                    if (debug) {
                        logger.debug("通过token访问接口，authentication为：: " + authentication);
                    }
                    //如果有那么去认证服务器加载用户信息
                    authentication = this.authenticate.authentication(authentication);
                }
                //如果头部没有token  那么检查session中时候有登陆信息
                if (authentication == null) {
                    authentication = this.authenticationSessionStore.loadAuthentication(request);
                }
                if (authentication != null) {
                    if (debug) {
                        logger.debug("认证成功: " + authentication);
                    }
                    SecurityContextHolder.setContext(authentication);
                }

            } catch (AuthenticationException e) {
                logger.error("认证失败: ", e);
                SecurityContextHolder.clearContext();
            }
        }
        chain.doFilter(request, response);

    }


}
